<?php
/**
 * Created by PhpStorm.
 * User: ytlmi
 * Date: 2017/10/19
 * Time: 12:06
 */

namespace org\util\wordtool;

class Word
{
    protected $style = [];
    protected $font = [];
    protected $body;
    protected $header;
    protected $footer;
    protected $temp;
    protected $tempPath = '';
    protected static $instance;//object 对象实例

    /*
     * 构造函数
     */
    public function __construct()
    {
        $this->tempPath = $_SERVER['DOCUMENT_ROOT'] . '/wordTemp';
    }

    /**
     * @return Word
     * 实例化当前类
     */
    static public function instance()
    {
//        if (is_null(self::$instance)) {
//            self::$instance = new static();
//        }
//        return self::$instance;
        return new static();
    }

    /**
     * @param mixed $font
     * @param array $style
     * @return Content
     * @throws \Exception
     */
    public function createTemp($font = null, $style = null)
    {
        $page = new \DOMDocument();
        return $this->temp = new Content($page,$font,$style);
    }

    /**
     * @param mixed $font
     * @param array $style
     * @return Content
     * @throws \Exception
     * 新建正文页面
     */
    public function createBody($font = null, $style = null)
    {
        $page = new \DOMDocument();
        $page->loadXML(StaticElement::PREXML_PAGE);
        $body = $page->getElementsByTagName('body')->item(0);
        $this->body = new Content($body, $font, $style);
        return $this->body;
    }

    /**
     * @param mixed $font
     * @param array $style
     * @return Content
     * @throws \Exception
     * 新建页眉
     */
    public function createHeader($font = null, $style = null)
    {
        $header = new \DOMDocument();
        $header->loadXML(StaticElement::PREXML_HEADER);
        $hdr = $header->getElementsByTagName('hdr')->item(0);
        return $this->header = new Content($hdr, $font, $style);
    }

    /**
     * @param mixed $font
     * @param array $style
     * @return Content
     * @throws \Exception
     * 新建页脚
     */
    public function createFooter($font = null, $style = null)
    {
        $header = new \DOMDocument();
        $header->loadXML(StaticElement::PREXML_FOOTER);
        $ftr = $header->getElementsByTagName('ftr')->item(0);
        return $this->footer = new Content($ftr, $font, $style);
    }

    /*
     * 保存各组件并生成压缩docx
     */
    public function saveDocx()
    {
        //TODO:检测多媒体元素并添加
        //document
        $this->body instanceof Content ? $document = $this->body->saveXML() : $document = StaticElement::PREXML_PAGE;
        $this->body->save($this->tempPath . '/doc.xml');
        //header
        $this->header instanceof Content ? $header = $this->header->saveXML() : $header = StaticElement::PREXML_HEADER;
        //footer
        $this->header instanceof Content ? $footer = $this->header->saveXML() : $footer = StaticElement::PREXML_FOOTER;

        //各组件文件名及内容
        $arr_zip = [
            '[Content_Types].xml' => StaticElement::CONTENT_TYPES,
            '_rels/.rels' => StaticElement::PREXML_RELS,
            'word/_rels/document.xml.rels' => StaticElement::PREXML_PAGE_RELS,
            'word/document.xml' => $document,
            'word/footer.xml' => $footer,
            'word/header.xml' => $header,
        ];

        //写入并保存
        $zipPath = $this->tempPath . '/word.docx';
        if (file_exists($zipPath)) unlink($zipPath);
        $zip = new \ZipArchive;
        if ($zip->open($zipPath, \ZipArchive::CREATE) === true) {
            foreach ($arr_zip as $localPath => $content) {
                $zip->addFromString($localPath, $content);
            }
            $zip->close();
        }
    }

    //打包压缩文件夹
    protected function addFileToZip($path, \ZipArchive $zip, $exceptPath = null)
    {
        $handler = opendir($path); //打开当前文件夹由$path指定。
        while (($filename = readdir($handler)) !== false) {
            if ($filename != "." && $filename != "..") {//文件夹文件名字为'.'和‘..’，不要对他们进行操作
                if (is_dir($path . '/' . $filename)) {// 如果读取的某个对象是文件夹，则递归
//                    var_dump($path.$filename);
                    $this->addFileToZip($path . '/' . $filename, $zip, $exceptPath);
                } else { //将文件加入zip对象
//                    var_dump($path . '/' . $filename);
                    $pathInZip = str_replace($exceptPath . '/', '', $path . '/' . $filename);
//                    var_dump($pathInZip);
                    $zip->addFile($path . '/' . $filename, $pathInZip);
                }
            }
        }
    }

    //递归删除文件夹
    protected function deleteDir($path)
    {
        $handler = opendir($path);
        while (($filename = readdir($handler)) !== false) {
            if ($filename != "." && $filename != "..") {//文件夹文件名字为'.'和'..'，不要对他们进行操作
                if (!is_dir($path . '/' . $filename)) {// 如果读取的某个对象是文件夹，则递归
                    unlink($path . '/' . $filename);
                } else {
                    $this->deleteDir($path . '/' . $filename);
                }
            }
        }
        closedir($handler);
        rmdir($path);
    }

    /**
     * @param array $content
     * @param string $templateName
     * @param string $saveName
     * @throws \Exception
     * 模板替换,内容$content为数组，键名为标记，键值为替换内容。
     * 替换内容可为字符串文字，也可为Content对象。
     * 替换内容为Content对象时，会将标记所在的<w:Pr/>节点替换为Content对象的DOMElement内容
     */
    public function setTemplate($content, $templateName, $saveName)
    {
        if (!is_array($content)) throw new \Exception('文档格式错误');
        if (!file_exists($templateName)) throw new \Exception('模板文件' . $templateName . '不存在');
        $info=pathinfo($templateName);
        $path=$info['dirname'];
        $extractPath = $path . DS . 'extract';
        $zip = new \ZipArchive;
        if ($zip->open($templateName) === TRUE) {
            $zip->extractTo($extractPath);
            $zip->close();
        }

        //文档标记数组分类
        $mark_str=[];
        $replace_str=[];
        $replace_dom=[];
        foreach ($content as $mark=>$replace) {
            if($replace instanceof Content){
                $replace_dom[$mark]=$replace;
            }else{
                $mark_str[]=$mark;
                $replace_str[]=$replace;
            }
        }

        //替换文本内容
        $doc = file_get_contents($extractPath . '/word/document.xml');
        $doc = str_replace($mark_str, $replace_str, $doc);

        //替换文档节点Node
        $dom = new \DOMDocument();
        $dom -> loadXML($doc);//加载
        foreach ($dom->getElementsByTagName('t') as $item) {//遍历节点寻找标记
            if(isset($replace_dom[$item->textContent])){//找到标记
                $dom_new = $replace_dom[$item->textContent]->getCurrentDOM();//新元素所在的DOM
                $thisPrNode=$item->parentNode->parentNode;//标记所在的<w:Pr/>节点
                foreach ($dom_new->childNodes as $node) {//新元素的每个节点
                    $newNode=$dom->importNode($node,true);//新节点导入当前DOMDocument
                    $thisPrNode->parentNode->insertBefore($newNode,$thisPrNode);//插入
                }
                $thisPrNode->parentNode->removeChild($thisPrNode);//删除所在的<w:Pr/>节点
            }
        }
        $dom->save($extractPath . '/word/document.xml');//保存

        //添加进压缩文件docx中
        $zip = new \ZipArchive;
        if (file_exists($path . '/temp.docx')) unlink($path . '/temp.docx');
        if ($zip->open($path . '/temp.docx', \ZipArchive::CREATE) === TRUE) {
            $this->addFileToZip($extractPath, $zip, $extractPath);
            $zip->close();
        }else{
            throw new \Exception('创建DOCX文档失败');
        }

        //移动、清理
        $this->deleteDir($extractPath);
        $info=pathinfo($saveName);
        if(!file_exists($info['dirname'])) mkdir($info['dirname']);
        rename($path . '/temp.docx',$saveName);
    }


}